package com.usian.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.usian.admin.mapper.AdChannelMapper;
import com.usian.admin.service.AdChannelService;
import com.usian.model.admin.dtos.ChannelDto;
import com.usian.model.admin.pojos.AdChannel;
import com.usian.model.common.dtos.PageResponseResult;
import com.usian.model.common.dtos.ResponseResult;
import com.usian.model.common.enums.AppHttpCodeEnum;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class AdChannelServiceImpl extends ServiceImpl<AdChannelMapper, AdChannel> implements AdChannelService {
    @Autowired
    private AdChannelMapper adChannelMapper;

    /**
     * 分页查询
     *
     * @param dto
     * @return responseResult
     */
    @Override
    public ResponseResult findByNameAndPage(ChannelDto dto) {
        //1.参数检测
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //分页参数检查
        dto.checkParam();
        //2.按照名称模糊分页查询
        Page page = new Page(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<AdChannel> lambdaQueryWrapper = new LambdaQueryWrapper();
        if (StringUtils.isNotBlank(dto.getName())) {
            lambdaQueryWrapper.like(AdChannel::getName, dto.getName());
        }
        IPage result = page(page, lambdaQueryWrapper);

        //3.结果封装
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) result.getTotal());
        responseResult.setData(result.getRecords());
        return responseResult;
    }

    /**
     * 新增频道
     *
     * @param adchannel
     * @return
     */
    @Transactional
    @Override
    public ResponseResult insert(AdChannel adchannel) {
        if (adchannel == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        adchannel.setCreatedTime(new Date());
        save(adchannel);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 修改
     *
     * @param adChannel
     * @return
     */
    @Transactional
    @Override
    public ResponseResult update1(AdChannel adChannel) {
        //1.检查参数
        if (null == adChannel || adChannel.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.修改
        int i = adChannelMapper.updateById(adChannel);
        if (i > 0) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SERVER_ERROR);
    }

    /**
     * 删除
     *
     * @param id
     * @return
     */
    @Transactional
    @Override
    public ResponseResult deleteById(Integer id) {
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.判断当前频道是否存在和是否有效
        AdChannel adChannel = getById(id);
        if (adChannel == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (adChannel.getStatus()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "频道有效无法删除");
        }
        //3.删除频道
        int i = adChannelMapper.deleteById(id);
        if (i > 0) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SERVER_ERROR);
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public ResponseResult findChannels() {

        //业务分析
        //先从缓存中取出数据 注意 保存的json字符串
        String channels = redisTemplate.opsForValue().get("channels");
        //加锁操作
        // synchronized (this){
        //拿到锁以后继续再从缓存中取进行判断 没有才去查询
//      分布式锁实现
        //去占坑，设置锁，存在互斥，只有一个能执行
        if (StringUtils.isEmpty(channels)) {
            //synchronized (this) {
            //再去查询一次确认没有
            return getRedissonResponseResult(channels);
        }
        // }
        System.out.println("缓存中有数据从缓存取出");
        // }

        ResponseResult responseResult = ResponseResult.okResult(channels);

        return responseResult;
    }

   /* private ResponseResult getJsonResponseResult(String channels) {

        synchronized (this){
            String channels1 = redisTemplate.opsForValue().get("channels");
            //如果有
            if (!StringUtils.isEmpty(channels1)) {
                //直接返回
                return ResponseResult.okResult(channels1);
            }
            System.out.println("查询了数据库");
            //缓存中没有再去查询数据库
            List<AdChannel> list = list();
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                channels = objectMapper.writeValueAsString(list);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //将值保存到缓存中
            redisTemplate.opsForValue().set("channels", channels, 1, TimeUnit.DAYS);
            return ResponseResult.okResult(channels);
        }

    }
    //redis分布式锁
    private ResponseResult getRedisResponseResult(String channels) {
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
        if (lock){
            //加锁成功
            //redisTemplate.expire("lock",30,TimeUnit.SECONDS);//30秒后自动删除
            String channels1 = redisTemplate.opsForValue().get("channels");
            //如果有
            if (!StringUtils.isEmpty(channels1)) {
                //直接返回
                return ResponseResult.okResult(channels);
            }
            System.out.println("查询了数据库");
            //缓存中没有再去查询数据库
            List<AdChannel> list = list();
            ObjectMapper objectMapper = new ObjectMapper();

            try {
                channels = objectMapper.writeValueAsString(list);
            } catch (IOException e) {
                e.printStackTrace();
            }

            //将值保存到缓存中
            redisTemplate.opsForValue().set("channels", channels, 1, TimeUnit.DAYS);
            //释放锁
            String s = redisTemplate.opsForValue().get("lock");
            if (uuid.equals(s)){
                redisTemplate.delete("lock");
            }
            return ResponseResult.okResult(channels);

            //返回结果
        }else {
            //加锁失败
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //重新自旋
            return  getJsonResponseResult(channels);
        }

    }*/
    @Autowired
    private RedissonClient redissonClient;
    private ResponseResult getRedissonResponseResult(String channels) {

        //redisson实现分布式锁
        RLock lock = redissonClient.getLock("redisson-channel");
        lock.lock();
        //加锁成功，获取锁
        //再去缓存中去取，再去判断
        String channels1 = redisTemplate.opsForValue().get("channels");
        if (!StringUtils.isEmpty(channels1)) {
//                    redisTemplate.opsForValue().set("channels",channels1,1,TimeUnit.DAYS);
            return ResponseResult.okResult(channels1);
        }
        //查询数据库
        System.out.println("查询数据库");
        List<AdChannel> list = list();
        //将数据保存到缓存中
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String s = objectMapper.writeValueAsString(list);
            redisTemplate.opsForValue().set("channels", s, 1, TimeUnit.DAYS);
            //如果业务时间超长，大于30秒，锁已经过期了，删除的时候删除哪个锁，删除谁的锁？
            return ResponseResult.okResult(s);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
        return null;
    }
}